/*
 * BuddySystem.cpp
 *
 *  Created on: Mar 2, 2011
 *      Author: jelle
 */
#include <stdlib.h>		// voor: rand
#include "asserts.h"		// voor: require
#include "BuddySystem.h"

// iemand vraagt om 'wanted' geheugen
const Area * BuddySystem::alloc(int wanted) {
	require( wanted > 0 ); // minstens "iets",
	require( wanted <= size ); // maar niet meer dan we kunnen hebben.

	Buddy* buddy = findSpace(wanted,1);
	std::cout << "returning 0 " << std::flush;
	if(buddy != 0){
		return buddy->getArea();
	}
	return 0;

}

Buddy* BuddySystem::findSpace(int size, int start){
	if(memoryAdministration[start]->getStatus() == FULL){
		return 0;
	}
	if(memoryAdministration[start]->getStatus() == SPLIT){
		Buddy* tempbuddy = findSpace(size,start*2);
		if(tempbuddy != 0){
			return tempbuddy;
		}else{
			tempbuddy = findSpace(size,start*2+1);
			if(tempbuddy != 0){
				return tempbuddy;
			}
			return 0;
		}
	}
	if(memoryAdministration[start]->getStatus() == EMPTY){
		int counter = 0;
		int tmpstart = start;
		while(tmpstart > 1){
			tmpstart = tmpstart/2;
			counter++;
		}
		int partsize = this->size/pow(2,counter);
		if(partsize < size){
			return 0;
		}
		if(partsize/2 >= size){
			memoryAdministration[start]->split();
			memoryAdministration[start*2] = new Buddy();
			memoryAdministration[start*2+1] = new Buddy();
			return findSpace(size,start*2);
		}else{
			int base = (start-pow(2,counter))*partsize;
			Area* area = new Area(base,partsize);
			memoryAdministration[start]->setArea(area);
			return memoryAdministration[start];
		}
	}
	return 0;
}

// iemand levert een gebied weer in
void BuddySystem::free(const Area * ap) {
	int firstInRow = size/ap->getSize();
	int matchedBuddy = firstInRow+(ap->getBase()/ap->getSize());
	memoryAdministration[matchedBuddy]->removeArea();

	joinBuddys(matchedBuddy);

	delete ap;
}

void BuddySystem::joinBuddys(int buddy){
	if(buddy%2 == 0){
		if(memoryAdministration[buddy+1]->getStatus() == EMPTY){
			delete memoryAdministration[buddy];
			delete memoryAdministration[buddy+1];
			memoryAdministration[buddy] = 0;
			memoryAdministration[buddy+1] = 0;
			memoryAdministration[buddy/2]->join();
			joinBuddys(buddy/2);
		}
	}else if(buddy > 1){
		if(memoryAdministration[buddy-1]->getStatus() == EMPTY){
			delete memoryAdministration[buddy];
			delete memoryAdministration[buddy-1];
			memoryAdministration[buddy] = 0;
			memoryAdministration[buddy-1] = 0;
			memoryAdministration[buddy/2]->join();
			joinBuddys(buddy/2);
		}
	}
}
